﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;

namespace LFSRecord2.Model.Sound
{
    /// <summary>
    /// This class lets you control and use wave out and in devices.
    /// </summary>
    public class WaveIO : IDisposable
    {
        private WaveOutDevice _waveOut;
        private int _waveOutBufSize = 16384;
        private byte _waveOutBufCount = 3;

        WaveFormatEx _waveFormat = null;
        public WaveFormatEx WaveFormat
        {
            get
            {
                if (_waveFormat == null)
                    _waveFormat = new WaveFormatEx(44100, 16, 2);
                return _waveFormat;
            }
            set
            {
                _waveFormat = value;
            }
        }
        
        //private WaveInDevice _waveIn;
        //private int _waveInBufSize = 16384;

        BufferFillEventHandler _bufferFillCallback;
        BufferPopulateEventHandler _bufferPopulateCallback;

        public int CurrentWaveOutDevice { get; set; }
        public int CurrentWaveInDevice { get; set; }

        public static int NumOutputDevices
        {
            get { return NativeMethods.waveOutGetNumDevs(); }
        }

        public static int NumInputDevices
        {
            get { return NativeMethods.waveInGetNumDevs(); }
        }

        public WaveIO()
        {
            CurrentWaveOutDevice = -1;
        }
        public WaveIO(BufferPopulateEventHandler bufferPopulateCallback, BufferFillEventHandler bufferFillCallback, int waveOutBufSize)
        {
            CurrentWaveOutDevice = -1;
            CurrentWaveInDevice = -1;
            _bufferPopulateCallback = bufferPopulateCallback;
            _bufferFillCallback = bufferFillCallback;
            _waveOutBufSize = waveOutBufSize;
        }
        ~WaveIO()
        {
            Dispose();
        }
        public void Dispose()
        {
            StopPlayback();
            StopRecord();
            _bufferFillCallback = null;
            _bufferPopulateCallback = null;

            GC.SuppressFinalize(this);
        }

        public static List<WaveOutCaps> EnumOutDevices()
        {
            List<WaveOutCaps> devices = new List<WaveOutCaps>();
            WaveOutCaps woc;
            WaveDeviceFormats wf;
            for (int a = -1; a < NumOutputDevices; a++)
            {
                woc = new WaveOutCaps();
                NativeMethods.waveOutGetDevCaps(a, ref woc, Marshal.SizeOf(woc));
                devices.Add(woc);
                wf = (WaveDeviceFormats)Enum.Parse(typeof(WaveDeviceFormats), woc.dwFormats.ToString(CultureInfo.InvariantCulture));
                //Debug.WriteLine("Out DevCaps : " + woc.szPname + " - NumChannels : {0}", woc.wChannels);
                //Debug.WriteLine("DevFormats : " + wf.ToString());
            }
            return devices;
        }

        public static List<WaveInCaps> EnumInDevices()
        {
            List<WaveInCaps> devices = new List<WaveInCaps>();
            WaveInCaps wic;
            WaveDeviceFormats wf;
            for (int a = -1; a < NumInputDevices; a++)
            {
                wic = new WaveInCaps();
                NativeMethods.waveInGetDevCaps(a, ref wic, Marshal.SizeOf(wic));
                devices.Add(wic);
                wf = (WaveDeviceFormats)Enum.Parse(typeof(WaveDeviceFormats), wic.dwFormats.ToString(CultureInfo.InvariantCulture));
                //Debug.WriteLine("In DevCaps : " + wic.szPname + " - NumChannels : {0}", wic.wChannels);
                //Debug.WriteLine("DevFormats : " + wf.ToString());
            }
            return devices;
        }

        public static WaveOutCaps DeviceCaps(int index)
        {
            if (index >= NumOutputDevices || index < -1)
                throw new Exception("Invalid device index");

            WaveOutCaps woc = new WaveOutCaps();
            NativeMethods.waveOutGetDevCaps(index, ref woc, Marshal.SizeOf(woc));
            return woc;
        }

        public static List<string> GetSampleRatesList(uint dwFormats)
        {
            List<string> sampleRates = new List<string>();

            uint flag;
            for (int a = 0; a < 20; a++)
            {
                flag = (uint)Math.Pow(2, a);
                if ((dwFormats & flag) > 0)
                {
                    switch (a)
                    {
                        //case 0:
                        //    sampleRates.Add("");
                        //    break;
                        //case 1:
                        //    sampleRates.Add("");
                        //    break;
                        //case 2:
                        //    sampleRates.Add("");
                        //    break;
                        case 3:
                            sampleRates.Add("11025");
                            break;
                        //case 4:
                        //    sampleRates.Add("");
                        //    break;
                        //case 5:
                        //    sampleRates.Add("");
                        //    break;
                        //case 6:
                        //    sampleRates.Add("");
                        //    break;
                        case 7:
                            sampleRates.Add("22050");
                            break;
                        //case 8:
                        //    sampleRates.Add("");
                        //    break;
                        //case 9:
                        //    sampleRates.Add("");
                        //    break;
                        //case 10:
                        //    sampleRates.Add("");
                        //    break;
                        case 11:
                            sampleRates.Add("44100");
                            break;
                        //case 12:
                        //    sampleRates.Add("");
                        //    break;
                        //case 13:
                        //    sampleRates.Add("");
                        //    break;
                        //case 14:
                        //    sampleRates.Add("");
                        //    break;
                        case 15:
                            sampleRates.Add("48000");
                            break;
                        //case 16:
                        //    sampleRates.Add("");
                        //    break;
                        //case 17:
                        //    sampleRates.Add("");
                        //    break;
                        //case 18:
                        //    sampleRates.Add("");
                        //    break;
                        case 19:
                            sampleRates.Add("96000");
                            break;
                    }
                }
            }

            return sampleRates;
        }

        public void StartPlayback()
        {
            StopPlayback();
            if (NumOutputDevices > 0 && _waveOut == null && _bufferFillCallback != null && _bufferPopulateCallback != null)
            {
                try
                {
                    _waveOut = new WaveOutDevice(CurrentWaveOutDevice, WaveFormat, _waveOutBufSize, _waveOutBufCount, _bufferPopulateCallback, _bufferFillCallback);
                }
                catch
                {
                    StopPlayback();
                    throw;
                }
            }
        }

        public void StopPlayback()
        {
            if (_waveOut != null)
            {
                try
                {
                    _waveOut.Dispose();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("stop-out dispose exception : " + ex.Message);
                }
                finally
                {
                    _waveOut = null;
                }
            }
        }

        public void StartRecord()
        {
        }

        public void StopRecord()
        {
        }
    }
}
